import { isHttpPath } from '../../utils/utils'
import { download } from '../../utils/http'
import { FileJson } from '../../types/type.d'
import { CSEvent, CSInterface } from 'csinterface-ts'
import { LastErrorResult } from '../../types/CEPEngine_extensions'
import { EventType } from '../../constants/event'
import { EventEmitter } from 'events'
import { HostType } from '../../constants/common'
import path from 'path'
import { copyFile } from 'fs-extra'

const cs = new CSInterface()

/**
 * Opens a page in the default system browser.
 *
 * Since 4.2.0
 *
 * @param url  The URL of the page/file to open, or the email address.
 * Must use HTTP/HTTPS/file/mailto protocol. For example:
 *   "http://www.adobe.com"
 *   "https://github.com"
 *   "file:///C:/log.txt"
 *   "mailto:test@adobe.com"
 *
 * @return One of these error codes:
 *      <ul>
 *          <li>NO_ERROR - 0</li>
 *          <li>ERR_UNKNOWN - 1</li>
 *          <li>ERR_INVALID_PARAMS - 2</li>
 *          <li>ERR_INVALID_URL - 201</li>
 *      </ul>
 */
export function openURLInDefaultBrowser (url: string): number {
  return cs.openURLInDefaultBrowser(url)
}

/**
 * Displays the OS File Open dialog, allowing the user to select files or directories.
 *
 * @param allowMultipleSelection When true, multiple files/folders can be selected.
 * @param chooseDirectory When true, only folders can be selected. When false, only
 *        files can be selected.
 * @param title Title of the open dialog.
 * @param initialPath Initial path to display in the dialog. Pass NULL or "" to
 *        display the last path chosen.
 * @param fileTypes The file extensions (without the dot) for the types
 *      of files that can be selected. Ignored when chooseDirectory=true.
 * @param friendlyFilePrefix String to put in front of the extensions
 *      of files that can be selected. Ignored when chooseDirectory=true. (win only)
 *      For example:
 *          fileTypes = ["gif", "jpg", "jpeg", "png", "bmp", "webp", "svg"];
 *          friendlyFilePrefix = "Images (*.gif;*.jpg;*.jpeg;*.png;*.bmp;*.webp;*.svg)";
 * @param prompt String for OK button (mac only, default is "Open" on mac, "Open" or "Select Folder" on win).
 *
 * @return An object with these properties:
 *      <ul><li>"data": An array of the full names of the selected files.</li>
 *          <li>"err": The status of the operation, one of
 *          <br>NO_ERROR
 *          <br>ERR_INVALID_PARAMS </li>
 *      </ul>
 **/
export function showOpenDialog (
  allowMultipleSelection: boolean,
  chooseDirectory: boolean,
  title: string,
  initialPath: string,
  fileTypes: string[],
  friendlyFilePrefix: string,
  prompt: string
): { data: string[]; err: LastErrorResult } {
  return window.cep.fs.showOpenDialogEx(
    allowMultipleSelection,
    chooseDirectory,
    title,
    initialPath,
    fileTypes,
    friendlyFilePrefix,
    prompt
  )
}

/**
 * Displays the OS File Save dialog, allowing the user to type in a file name.
 *
 * @param title Title of the save dialog.
 * @param initialPath Initial path to display in the dialog. Pass NULL or "" to
 *        display the last path chosen.
 * @param fileTypes The file extensions (without the dot) for the types
 *      of files that can be selected.
 * @param defaultName String to start with for the file name.
 * @param friendlyFilePrefix String to put in front of the extensions of files that can be selected. (win only)
 *      For example:
 *          fileTypes = ["gif", "jpg", "jpeg", "png", "bmp", "webp", "svg"];
 *          friendlyFilePrefix = "Images (*.gif;*.jpg;*.jpeg;*.png;*.bmp;*.webp;*.svg)";
 * @param prompt String for Save button (mac only, default is "Save" on mac and win).
 * @param nameFieldLabel String displayed in front of the file name text field (mac only, "File name:" on win).
 *
 * @return An object with these properties:
 *      <ul><li>"data": The file path selected to save at or "" if canceled</li>
 *          <li>"err": The status of the operation, one of
 *          <br>NO_ERROR
 *          <br>ERR_INVALID_PARAMS </li>
 *      </ul>
 **/
export function showSaveDialog (
  title: string,
  initialPath: string,
  fileTypes: string[],
  defaultName: string,
  friendlyFilePrefix: string,
  prompt: string,
  nameFieldLabel: string
): { data: string; err: LastErrorResult } {
  return window.cep.fs.showSaveDialogEx(
    title,
    initialPath,
    fileTypes,
    defaultName,
    friendlyFilePrefix,
    prompt,
    nameFieldLabel
  )
}

/**
 * Register an interest in some key events to prevent them from being sent to the host application.
 *
 * This function works with modeless extensions and panel extensions.
 * Generally all the key events will be sent to the host application for these two extensions if the current focused element
 * is not text input or dropdown,
 * If you want to intercept some key events and want them to be handled in the extension, please call this function
 * in advance to prevent them being sent to the host application.
 *
 * Since 6.1.0
 *
 * @param keyEventsInterest      A JSON string describing those key events you are interested in. A null object or
                                 an empty string will lead to removing the interest
 *
 * This JSON string should be an array, each object has following keys:
 *
 * keyCode:  [Required] represents an OS system dependent virtual key code identifying
 *           the unmodified value of the pressed key.
 * ctrlKey:  [optional] a Boolean that indicates if the control key was pressed (true) or not (false) when the event occurred.
 * altKey:   [optional] a Boolean that indicates if the alt key was pressed (true) or not (false) when the event occurred.
 * shiftKey: [optional] a Boolean that indicates if the shift key was pressed (true) or not (false) when the event occurred.
 * metaKey:  [optional] (Mac Only) a Boolean that indicates if the Meta key was pressed (true) or not (false) when the event occurred.
 *                      On Macintosh keyboards, this is the command key. To detect Windows key on Windows, please use keyCode instead.
 * An example JSON string:
 *
 * [
 *     {
 *         "keyCode": 48
 *     },
 *     {
 *         "keyCode": 123,
 *         "ctrlKey": true
 *     },
 *     {
 *         "keyCode": 123,
 *         "ctrlKey": true,
 *         "metaKey": true
 *     }
 * ]
 *
 */
export function registerKeyEventsInterest (keyEventsInterest: string) {
  return cs.registerKeyEventsInterest(keyEventsInterest)
}

/**
 * Get the title of the extension window.
 * This function works with modal and modeless extensions in all Adobe products, and panel extensions in Photoshop, InDesign, InCopy, Illustrator, Flash Pro and Dreamweaver.
 *
 * Since 6.1.0
 *
 * @return The window title.
 */
export function getWindowTitle () {
  return cs.getWindowTitle()
}

/**
 * Set the title of the extension window.
 * This function works with modal and modeless extensions in all Adobe products, and panel extensions in Photoshop, InDesign, InCopy, Illustrator, Flash Pro and Dreamweaver.
 *
 * Since 6.1.0
 *
 * @param title The window title.
 */
export function setWindowTitle (title: string) {
  return cs.setWindowTitle(title)
}

/**
 * Registers an interest in a CEP event of a particular type, and
 * assigns an event handler.
 * The event infrastructure notifies your extension when events of this type occur,
 * passing the event object to the registered handler function.
 *
 * @param type     The name of the event type of interest.
 * @param listener The JavaScript handler function or method.
 * @param obj      Optional, the object containing the handler method, if any.
 *         Default is null.
 */
export function addEventListener (
  type: string,
  listener: Function,
  obj: { handler?: Function; once?: boolean } | null = null
) {
  let handler = listener
  if (obj && obj.once) {
    handler = function () {
      listener(...arguments)
      cs.removeEventListener(type, handler)
    }
  }
  return cs.addEventListener(type, handler, obj)
}

/**
 * Removes a registered event listener.
 *
 * @param type      The name of the event type of interest.
 * @param listener  The JavaScript handler function or method that was registered.
 * @param obj       Optional, the object containing the handler method, if any.
 *          Default is null.
 */
export function removeEventListener (
  type: string,
  listener: Function,
  obj: { handler: Function } | null = null
) {
  return cs.removeEventListener(type, listener, obj)
}

/**
 * Triggers a CEP event programmatically. Yoy can use it to dispatch
 * an event of a predefined type, or of a type you have defined.
 *
 * @param event A \c CSEvent object.
 */
export function dispatchEvent (event: CSEvent) {
  return cs.dispatchEvent(event)
}

/**
 * Loads and launches another extension, or activates the extension if it is already loaded.
 *
 * @param extensionId       The extension's unique identifier.
 * @param startupParams     Not currently used, pass "".
 *
 * @example
 * To launch the extension "help" with ID "HLP" from this extension, call:
 * <code>requestOpenExtension("HLP", ""); </code>
 *
 */
export function requestOpenExtension (extensionId: string, params: string) {
  return cs.requestOpenExtension(extensionId, params)
}

/** Closes this extension. */
export function closeExtension () {
  return cs.closeExtension()
}

/**
 * Retrieves a path for which a constant is defined in the system.
 *
 * @param pathType The path-type constant defined in \c #SystemPath userData|commonFiles|myDocuments|extension|hostApplication,
 *
 * @return The platform-specific system path string.
 */
export function getSystemPath (pathType: string) {
  return cs.getSystemPath(pathType)
}

/**
 * 获取插件安装路径
 * eg: C:/Program Files (x86)/Common Files/Adobe/CEP/extensions/dev-pr
 * @returns
 */
export function getExtensionRootPath () {
  return getSystemPath('extension')
}

/**
 * 判断是否是在windows系统
 *
 * @export
 * @return
 */
export function isWindows (): boolean {
  const osPath = cs.getOSInformation()
  return osPath.includes('Windows')
}

/**
 * 获取app环境信息
 *
 * @export
 * @return {*} 
 */
export function getHostEnvironment () {
  return cs.getHostEnvironment()
}

/**
 * 加载jsx代码文件
 *
 * @export
 * @param path jsx 路径或者目录
 * @return
 */
export function loadJsxFile (path: string): Promise<string> {
  if (path.endsWith('.jsx')) {
    return evalCommonMethod('evalFile', path)
  }
  return evalCommonMethod('evalFiles', path)
}

/**
 * 执行jsx代码片段
 *
 * @export
 * @param method
 * @param args
 * @return
 */
export function evalScript (script: string): Promise<string> {
  return new Promise((resolve) => {
    cs.evalScript(script, resolve)
  })
}

/**
 *
 *
 * @param method 方法名
 * @param args 参数
 * @return
 */

/**
 * 根据宿主类型构造 jsx 方法调用器；
 * 调用器传入参数，和执行返回结果统一处理
 * @param host 宿主类型
 * @returns
 */
export function methodEvaler (host: HostType) {
  const ROOT_FUN = '$._evalMethod'
  return function _evalMethod (method: string, ...args: any[]) {
    const script = `${ROOT_FUN}("${host}", "${method}"${dealArgs(args)})`
    console.log(script)
    return evalScript(script).then((res) => {
      if (res === 'EvalScript error.') {
        const errMsg = `EvalScript ${method} error, EvalScript error.`
        return Promise.reject(new Error(errMsg))
      }
      const result = JSON.parse(res)
      if (result._isJsxError) {
        // 约定的错误标识
        console.error(result.data)
        return Promise.reject(new Error(result.data.message))
      }
      return result.data
    })
  }
  function dealArgs (args: any[]) {
    const argsStr = args
      .map((arg) => {
        arg = JSON.stringify({ data: arg })
        return `"${encodeURIComponent(arg)}"`
      })
      .join(',')
    return argsStr ? ','.concat(argsStr) : ''
  }
}

/**
 * 执行公共 jsx 方法
 *
 * @export
 * @param method
 * @param args
 * @return
 */
export function evalCommonMethod (method: string, ...args: any[]) {
  let evalFun
  return (function () {
    if (!evalFun) evalFun = methodEvaler(HostType.common)
    return evalFun.apply(null, [method, ...args])
  })()
}

/**
 * 下载文件
 *
 * @export
 * @param 原文件地址, 可以是http url, 也可以是文件系统路径
 * @param 目标地址, 不提供着保存到临时文件目录
 * @return
 */
export async function downloadFile (from: string, to?: string) {
  if (!to) {
    // 还需要创建目录
    to = path.join(
      getSystemPath('userData'),
      'adobe-cep-core',
      +new Date() + '',
      path.basename(from.split('?')[0])
    ) // 是temp吗
    console.log(to)
  }
  if (isHttpPath(from)) {
    return download(from, path.dirname(to), path.basename(to))
  } else {
    await copyFile(from, to)
    return to
  }
}

export function getHost () {
  return evalCommonMethod('getHost')
}

/**
 * 事件监听与派发
 *
 */
export const eventBus = new EventEmitter()

/**
 * 事件列表
 *
 */
export const EVENT_TYPES = EventType

/**
 * 写 fake 文件
 * @param data
 * @returns
 */
export function genFakeFile (data: FileJson) {
  return ''
}

type Job = {
  taskId: number
  progress: number
  status: string
  outputFilePath?: string
  msg?: string
}
/**
 * 合成任务列表
 *
 */
export const renderingTasks = new Map<string, Job>()
